/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.data.jdbc.impl;

import io.iec.edp.caf.commons.dataaccess.DbType;
import io.iec.edp.caf.data.jdbc.Database;
import io.iec.edp.caf.data.jdbc.handler.ResultSetHandler;
import io.iec.edp.caf.data.source.DataSourceTypeRecognizer;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.util.Assert;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class BaseDatabaseImpl implements Database {

    private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    private final CafDataSource cafDataSource;

    private final Connection connection;
    private List<Statement> statementCache = new ArrayList<>();
    private List<PreparedStatement> preparedStatementCache = new ArrayList<>();

    public BaseDatabaseImpl(Connection connection) {
        this.connection = connection;
        this.cafDataSource = new CafDataSource(connection);
        this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(cafDataSource);
}

    @SneakyThrows
    @Override
    public DbType getDbType() {
        var url = this.connection.getMetaData().getURL();
        return DataSourceTypeRecognizer.recognize(url);
    }

    @Override
    public String getConcatOperator() throws SQLException {
        throw new SQLException("Unsupported method");
    }

    @Override
    public String getNewIdFunc() throws SQLException {
        throw new SQLException("Unsupported method");
    }

    @Override
    public String getDBDataTimeFunc() throws SQLException {
        throw new SQLException("Unsupported method");
    }

    @Override
    public String getSubStrFunc() throws SQLException {
        throw new SQLException("Unsupported method");
    }

    @Override
    public String getIsNullFunc() throws SQLException {
        throw new SQLException("Unsupported method");
    }

    @Override
    public String getStrLenFunc() throws SQLException {
        throw new SQLException("Unsupported method");
    }

    @Override
    public void beginTrans() throws SQLException{
        var connection = this.cafDataSource.getConnection();
        if(connection!=null && connection.isClosed()==false){
            connection.setAutoCommit(false);
        }
    }

    @Override
    public void commitTrans() throws SQLException {
        var connection = this.cafDataSource.getConnection();
        if(connection!=null && connection.isClosed()==false){
            if(connection.getAutoCommit()==false){
                connection.commit();
            }else{
                throw new SQLException("The current link AutoCommit is true, there is no need to manually commit the transaction");
            }
        }
    }

    @Override
    public void rollbackTrans() throws SQLException {
        var connection = this.cafDataSource.getConnection();
        if(connection!=null && connection.isClosed()==false){
            if(connection.getAutoCommit()==false){
                connection.rollback();
            }else{
                throw new SQLException("The current link AutoCommit is true, there is no need to manually commit the transaction");
            }
        }
    }

    @Override
    public <T> T query(String sql, Class<T> clazz, Object... params) throws SQLException {
        return null;
    }

    @Override
    public <T> T query(String sql, Class<T> clazz, List<Object> params) throws SQLException {
        return null;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> handler, Object... params) throws SQLException {
        return null;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> handler, List<Object> params) throws SQLException {
        return null;
    }

    @Override
    public <T> List<T> queryForList(String sql, Class<T> clazz, Object... params) throws SQLException {
        return null;
    }

    @Override
    public <T> List<T> queryForList(String sql, Class<T> clazz, List<Object> params) throws SQLException {
        return null;
    }

    @Override
    public Map<String, Object> queryForMap(String sql, Object... params) throws SQLException {
        return null;
    }

    @Override
    public Map<String, Object> queryForMap(String sql, List<Object> params) throws SQLException {
        return null;
    }

    @Override
    public List<Map<String, Object>> queryForMapList(String sql, Object... params) throws SQLException {
        return null;
    }

    @Override
    public List<Map<String, Object>> queryForMapList(String sql, List<Object> params) throws SQLException {
        return null;
    }

    @Override
    public int update(String sql, Object... params) throws SQLException {
        return 0;
    }

    @Override
    public int update(String sql, List<Object> params) throws SQLException {
        return 0;
    }

    @Override
    public Boolean execute(String sql) throws SQLException {
        Assert.isTrue(!connection.isClosed(), "Connection is already closed.");
        Statement statement = connection.createStatement();
        var ret = statement.execute(sql);
        if(statement!=null){
            statement.close();
        }
        return ret;
    }

    @Override
    public Boolean execute(String sql, List<Object> paramList) throws SQLException {
        return null;
    }

    @Override
    public Boolean execute(String sql, Map<String, Object> paramMap) throws SQLException {
        return null;
    }

    @Override
    public JdbcTemplate getJdbcTemplate() {
        return this.namedParameterJdbcTemplate.getJdbcTemplate();
    }

    @Override
    public void close() throws Exception {

        try{
            this.closeStatementCache();
        }catch (SQLException e){
            log.error("Statement close failure:"+e.getMessage(),e);
        }

        try{
            this.closePreparedStatementCache();
        }catch (SQLException e){
            log.error("PreparedStatement close failure:"+e.getMessage(),e);
        }

        this.cafDataSource.release();
    }

    /**
     * 关闭statement
     * @throws SQLException
     */
    private void closeStatementCache() throws SQLException{
        for(Statement statement:statementCache){
            if(statement!=null){
                statement.close();
            }
        }


    }

    /**
     * 关闭preparedStatement
     * @throws SQLException
     */
    private void closePreparedStatementCache() throws SQLException {
        for(PreparedStatement preparedStatement:preparedStatementCache){
            if(preparedStatement!=null){
                preparedStatement.close();
            }
        }
    }
}
